from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
import subprocess
import socket
from config import SearchParams_ctrip,SearchParams_fliggy,SearchParams_ly
import csv

def is_port_in_use(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex(('localhost', port)) == 0

def launch_chrome():
    if is_port_in_use(9222):
        print("端口 9222 已被占用，请释放该端口。")
        return
    chrome_path = "C:/Program Files/Google/Chrome/Application/chrome.exe"
    url = "http://127.0.0.1:5000"
    args = [
                chrome_path,
                "--remote-debugging-port=9222",
                "--user-data-dir=C:\\chromedevprofile",  # 注意双反斜杠
                url
            ]
    try:
        subprocess.Popen(args)
        time.sleep(2)  
    except Exception as e:
        print("启动 Chrome 浏览器时出现错误:", e)

def find_ctrip(params):

    print("正在获取携程数据...")
    chrome_options = Options()
    chrome_options.add_argument("--disable-gpu")
    chrome_options.debugger_address = "127.0.0.1:9222"  
    service = Service(executable_path="D:/chromedriver-win64/chromedriver.exe")  # 路径根据实际位置调整
    driver = webdriver.Chrome(service=service, options=chrome_options)

    base_url = "https://hotels.ctrip.com/hotels/list"
    full_url = f"{base_url}?{params.to_query()}"
    driver.execute_script(f"window.open('{full_url}');")

    driver.switch_to.window(driver.window_handles[-1])
    WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.TAG_NAME, 'body'))
    )
    print("已打开网页")

    try:
        time.sleep(1)
        # 获取酒店信息: 模拟慢速下滑页面，确保加载全部数据（从当前滚动位置继续下滑）
        current_pos = driver.execute_script("return window.pageYOffset")
        step = 125  # 每次下滑100像素，可根据需要调整
        click_count = 0  # 记录点击“搜索更多酒店”次数
        COUNT=4
        while True:
            new_height = driver.execute_script("return document.body.scrollHeight")
            # 从当前滚动位置滑动到页面底部
            while current_pos < new_height:
                driver.execute_script("window.scrollTo(0, arguments[0]);", current_pos)
                current_pos += step
                time.sleep(0.2)  # 每次滚动后短暂停留
            time.sleep(1)  # 滚动完成后等待页面加载新内容
            updated_height = driver.execute_script("return document.body.scrollHeight")
            # 判断是否出现“搜索更多酒店”按钮
            try:
                more_btn = driver.find_element(By.XPATH, '/html/body/div[2]/div[2]/div/div/section/div[2]/ul/div[2]/div/span')
                if more_btn.is_displayed() and click_count < COUNT:
                    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", more_btn)
                    time.sleep(1)  # 等待页面滚动到按钮位置
                    more_btn.click()
                    click_count += 1
                    time.sleep(2)  # 点击后等待更多内容加载
                    # 更新当前滚动位置，继续下滑
                    current_pos = driver.execute_script("return window.pageYOffset")
                    continue
            except Exception:
                pass
            if updated_height == new_height and click_count >= COUNT:
                break


        hotel_elements = driver.find_elements(By.XPATH, '//span[@class="hotelName"]')
        hotels_data = {}
        for hotel in hotel_elements:
            name = hotel.text
            # 定位酒店所在 li 容器，可作为统一查找其他信息的起点
            container = hotel.find_element(By.XPATH, "ancestor::li[1]")
            try:
                price = container.find_element(By.XPATH, ".//span[@class='sale']").text
            except Exception:
                price = "未获取到价格"
            try:
                rating = container.find_element(By.XPATH, ".//div[contains(@class, 'comment-score-c')]/span[contains(@class, 'score')]").text
            except Exception:
                rating = "未获取到评分"
            try:
                comment = container.find_element(By.XPATH, ".//span[contains(@class, 'comment-num')]").text
            except Exception:
                comment = "未获取到点评数"
            try:
                distance = container.find_element(By.XPATH, ".//span[contains(@class, 'position-desc')]").text
            except Exception:
                distance = "未获取到距离"

            hotels_data[name] = {
                "platform": "ctrip",
                "price": price,
                "rating": rating,
                "comment": comment,
                "distance": distance
            } 
        print("已获取酒店信息，共计", len(hotel_elements), "条数据。")

        csv_file = "other/hotels_data_ctrip.csv"
        with open(csv_file, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=["name", "platform", "price", "rating", "comment", "distance"])
            writer.writeheader()
            for name, info in hotels_data.items():
                row = {"name": name}
                row.update(info)
                writer.writerow(row)

    except Exception as e:
        print("在操作过程中出现错误:", e)
    finally:
        driver.quit()


def find_fliggy(params):

    print("正在获取飞猪数据...")
    chrome_options = Options()
    chrome_options.add_argument("--disable-gpu")
    chrome_options.debugger_address = "127.0.0.1:9222"  
    service = Service(executable_path="D:/chromedriver-win64/chromedriver.exe")  # 路径根据实际位置调整
    driver = webdriver.Chrome(service=service, options=chrome_options)

    base_url = "https://hotel.fliggy.com/hotel_list3.htm"
    full_url = f"{base_url}?{params.to_query()}"
    driver.execute_script(f"window.open('{full_url}');")

    driver.switch_to.window(driver.window_handles[-1])
    WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.TAG_NAME, 'body'))
    )
    print("已打开网页")

    try:
        time.sleep(1)
        # 获取酒店信息: 模拟慢速下滑页面，确保加载全部数据（从当前滚动位置继续下滑）
        current_pos = driver.execute_script("return window.pageYOffset")
        step = 125  # 每次下滑100像素，可根据需要调整
        click_count = 0  # 记录点击“搜索更多酒店”次数
        COUNT=5
        hotels_data = {}
        def collect_current_page_data():
            # 收集当前页面的酒店数据
            hotel_elements = driver.find_elements(By.XPATH, '//div[@class="list-row J_ListRow J_LazyZoom"]')
            for hotel_container in hotel_elements:
                try:
                    # 获取酒店名称
                    name = hotel_container.find_element(By.XPATH, ".//div[@class='row-center']//h5[@class='row-title']/a").text
                    if name in hotels_data:  # 如果已存在相同名称的酒店，跳过
                        continue
                    
                    try:
                        # 获取价格，移除¥符号
                        price = hotel_container.find_element(By.XPATH, ".//span[@class='pi-price pi-price-lg']").text
                        price = price.replace('¥', '').strip()
                    except Exception:
                        price = "未获取到价格"
                        
                    try:
                        # 获取评分
                        rating = hotel_container.find_element(By.XPATH, ".//a[@class='comment-score']//p[@class='score']/span[@class='value']").text
                    except Exception:
                        rating = "未获取到评分"
                        
                    try:
                        # 获取评论数
                        comment = hotel_container.find_element(By.XPATH, ".//p[@class='comment']/span[@class='volume']").text
                    except Exception:
                        comment = "未获取到点评数"
                        
                    try:
                        # 获取地址
                        distance = hotel_container.find_element(By.XPATH, ".//div[@class='row-center']//p[@class='row-address']//span[@class='address']").text
                    except Exception:
                        distance = "未获取到距离"

                    hotels_data[name] = {
                        "platform": "fliggy",
                        "price": price,
                        "rating": rating,
                        "comment": comment,
                        "distance": distance
                    }
                except Exception as e:
                    print(f"处理酒店数据时出错: {e}")
                    continue
                    
            print(f"当前页面收集到 {len(hotel_elements)} 条酒店数据")

            
        while True:
            new_height = driver.execute_script("return document.body.scrollHeight")
            # 从当前滚动位置滑动到页面底部
            while current_pos < new_height:
                driver.execute_script("window.scrollTo(0, arguments[0]);", current_pos)
                current_pos += step
                time.sleep(0.2)  # 每次滚动后短暂停留
            time.sleep(0.5)  # 滚动完成后等待页面加载新内容
            updated_height = driver.execute_script("return document.body.scrollHeight")
            collect_current_page_data()
            # 判断是否需要点击分页按钮
            try:
                if click_count < COUNT:
                    # 获取所有分页按钮
                    page_buttons = driver.find_elements(By.XPATH, '//a[@class="pi-pagination-num"]')
                    total_pages = len(page_buttons)
                    
                    if total_pages > 0 and click_count < total_pages:  # 确保不超过最大页数
                        # 查找并点击下一页按钮
                        page_btn = driver.find_element(By.XPATH, f'//a[@class="pi-pagination-num"][text()="{click_count + 2}"]')
                        if page_btn.is_displayed():
                            driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", page_btn)
                            time.sleep(1)  # 等待页面滚动到按钮位置
                            page_btn.click()
                            click_count += 1
                            time.sleep(2)  # 点击后等待新页面加载
                            # 重置滚动位置
                            current_pos = 0
                            continue
            except Exception as e:
                print(f"点击分页按钮时出错: {e}")
                pass
            
            if updated_height == new_height and (click_count >= COUNT or click_count >= total_pages):
                collect_current_page_data()
                break

        csv_file = "other/hotels_data_fliggy.csv"
        with open(csv_file, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=["name", "platform", "price", "rating", "comment", "distance"])
            writer.writeheader()
            for name, info in hotels_data.items():
                row = {"name": name}
                row.update(info)
                writer.writerow(row)

    except Exception as e:
        print("在操作过程中出现错误:", e)
    finally:
        driver.quit()

def collect_hotel_data(driver):
    # 获取所有酒店元素
    hotel_elements = driver.find_elements(By.XPATH, "//li[@class='flex justify-between w-[1040px]']")
    hotels_data = {}
    
    for hotel_container in hotel_elements:
        try:
            # 获取酒店名称
            name = hotel_container.find_element(
                By.XPATH,
                ".//dd[contains(@class, 'max-w-[520px]')]"
            ).text
            
            if name in hotels_data:  # 如果已存在相同名称的酒店，跳过
                continue
            
            try:
                # 获取价格
                price = hotel_container.find_element(
                    By.XPATH,
                    ".//span[contains(@class, 'text-Color-Uasge-TextIcon-color-price')]"
                ).text.replace('¥', '').split('起')[0].strip()
            except Exception:
                price = "未获取到价格"
                
            try:
                # 获取评分
                rating = hotel_container.find_element(
                    By.XPATH,
                    ".//div[contains(@class, 'commentScore')]"
                ).text
            except Exception:
                rating = "未获取到评分"
                
            try:
                # 获取评论数
                comment = hotel_container.find_element(
                    By.XPATH,
                    ".//div[contains(@class, 'text-black-style')]/em[contains(text(), '条点评')]"
                ).text.replace('条点评', '')
            except Exception:
                comment = "未获取到点评数"
                
            try:
                # 获取距离信息
                distance = hotel_container.find_element(
                    By.XPATH,
                    ".//div[contains(@class, 'text-[16px]') and contains(@class, 'text-Color-Uasge-TextIcon-color-secondary') and contains(@class, 'opacity-[50%]') and contains(@class, 'mb-[16px]')]"
                ).text
            except Exception:
                distance = "未获取到距离"

            hotels_data[name] = {
                "platform": "ly",
                "price": price,
                "rating": rating,
                "comment": comment,
                "distance": distance
            }
        except Exception as e:
            print(f"处理酒店数据时出错: {e}")
            continue
            
    print(f"当前页面收集到 {len(hotels_data)} 条酒店数据")
    return hotels_data

def find_ly(params):

    print("正在获取同程数据...")
    chrome_options = Options()
    chrome_options.add_argument("--disable-gpu")
    chrome_options.debugger_address = "127.0.0.1:9222"  
    service = Service(executable_path="D:/chromedriver-win64/chromedriver.exe")  # 路径根据实际位置调整
    driver = webdriver.Chrome(service=service, options=chrome_options)
    base_url ="https://www.ly.com/hotel/hotellist"
    full_url = f"{base_url}?{params.to_query()}"
    driver.execute_script(f"window.open('{full_url}');")
    driver.switch_to.window(driver.window_handles[-1])
    WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.TAG_NAME, 'body'))
    )
    print("已打开网页")

    try:
        time.sleep(1)
        # 获取酒店信息: 模拟慢速下滑页面，确保加载全部数据（从当前滚动位置继续下滑）
        current_pos = driver.execute_script("return window.pageYOffset")
        step = 125  # 每次下滑100像素，可根据需要调整
        click_count = 0  # 记录点击“搜索更多酒店”次数
        COUNT=1
        while True:
            new_height = driver.execute_script("return document.body.scrollHeight")
            # 从当前滚动位置滑动到页面底部
            while current_pos < new_height:
                driver.execute_script("window.scrollTo(0, arguments[0]);", current_pos)
                current_pos += step
                time.sleep(0.2)  # 每次滚动后短暂停留
            time.sleep(1)  # 滚动完成后等待页面加载新内容
            updated_height = driver.execute_script("return document.body.scrollHeight")
            # 判断是否出现“搜索更多酒店”按钮
            try:
                css_selector = "div.flex.items-center.justify-center.mx-auto.my-0.cursor-pointer"
                more_btn = driver.find_element(By.CSS_SELECTOR, css_selector)
                if more_btn.is_displayed() and click_count < COUNT:
                    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", more_btn)
                    time.sleep(1)  # 等待页面滚动到按钮位置
                    more_btn.click()
                    click_count += 1
                    time.sleep(2)  # 点击后等待更多内容加载
                    # 更新当前滚动位置，继续下滑
                    current_pos = driver.execute_script("return window.pageYOffset")
                    continue
            except Exception:
                pass
            if updated_height == new_height and click_count >= COUNT:
                break


        hotels_data = collect_hotel_data(driver)
        csv_file = "other/hotels_data_ly.csv"
        with open(csv_file, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=["name", "platform", "price", "rating", "comment", "distance"])
            writer.writeheader()
            for name, info in hotels_data.items():
                row = {"name": name}
                row.update(info)
                writer.writerow(row)

    except Exception as e:
        print("在操作过程中出现错误:", e)
    finally:
        driver.quit()







if __name__ == "__main__":
    params = SearchParams_ctrip(checkin="2025/02/27", checkout="2025/03/02",destination="北京",detailDestination="阜外医院")
    launch_chrome()
    find_ctrip(params)
    